/**
 * 
 */
package totalFileSize;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author mfan
 *
 */
public class TotalFileSize {

	/**
	 * Calculate total file size of a give path
	 * 
	 * @param path
	 * @throws IOException 
	 */
	public static long getTotalFileSizeSequential(final File path) throws IOException {

		if (path.isFile()) {
			return path.length();
		}
		
		long totalFileSize = 0;
		
		final File[] subFiles = path.listFiles();
		if (subFiles != null) {
			for (File aSubFile : subFiles) {
				totalFileSize += getTotalFileSizeSequential(aSubFile);
			}
		}
		
		return totalFileSize;
	}
	
	/**
	 * @param threadPool
	 * @param path
	 * @return
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @throws TimeoutException
	 */
	public static long getTotalFileSizeConcurrent(final ExecutorService threadPool, final File path) throws InterruptedException, ExecutionException, TimeoutException {
		
		if (path.isFile()) {
			return path.length();
		}
		
		final List<Future<Long>> results = new ArrayList<>();
		final File[] subPaths = path.listFiles();
		if (subPaths != null) {
			for (final File aSubPath : subPaths) {
				results.add(threadPool.submit(new Callable<Long>() {

					@Override
					public Long call() throws Exception {
						// TODO Auto-generated method stub
						return getTotalFileSizeConcurrent(threadPool, aSubPath);
					}

				}));
			}
		}
	
		long totalFileSize = 0;
		for (Future<Long> aResult : results) {
			totalFileSize += aResult.get(100, TimeUnit.SECONDS);
		}
		return totalFileSize;
	}
	
	public static long tryGetTotalFileSizeConcurrent(final File path) throws InterruptedException, ExecutionException, TimeoutException {
	
		final ExecutorService threadPool = Executors.newFixedThreadPool(20);
		
		try {
			return getTotalFileSizeConcurrent(threadPool, path);
		} finally {
			threadPool.shutdown();
		}
		
	}
	
	//-------------------------------------------
	public static void main(String[] args) throws IOException, InterruptedException, ExecutionException, TimeoutException {
		
		System.out.println("Start calculating total file size...");
		final long start = System.nanoTime();
		final String pathCalculateTotalFileSize = "/lib";
		final long totalFileSize = TotalFileSize.getTotalFileSizeSequential(new File(pathCalculateTotalFileSize));
//		final long totalFileSize = TotalFileSize.tryGetTotalFileSizeConcurrent(new File(pathCalculateTotalFileSize));
		final long end = System.nanoTime();
		System.out.printf("The total size of path: %s is %d.%n", pathCalculateTotalFileSize, totalFileSize);
		System.out.println("Time used: " + (end - start)/1.0e9);
	}
	
}
